import { Color, PlaneGeometry, DoubleSide, Mesh, ShaderMaterial, Vector3 } from "three";
import Effect from "./Effect";
import RippleWallShader from "../../renderExtension/Material/shaders/chunks/RippleWallShader";
import { Point } from "../../renderExtension/types/Point";
import Const from "../../application/constants/Const";
import Events from "../../application/constants/Events";
import Helper from "../../utils/Helper";
import { App } from "../../application";

/**
 * 波纹围墙动效组件
 */
export default class RippleWall extends Effect {
    /**
     * 波纹围墙几何体
     */
    private planeMeshs = [];
    private uniforms;
    /**
     * 波纹围墙中心位置
     */
    public center;
    /**
     * 波纹围墙颜色
     */
    public color;
    /**
     * 闪烁速度，单位为毫秒
     */ 
    public time = 1000;
    /**
     * 频率信息
     */
    //public hiz;
    /**
     * 若干单元组成波纹光柱
     */
    public num = 3;
    /**
     * 波纹围墙高度
     */
    public height = 50;
    /**
     * 波纹围墙位置
     */
    public positions = [];
    /**
     * 记录组件创建时间
     */
    //private _time = new Date().getTime();
    /**
     * 波纹围墙海拔高度
     */
    public altitude = 1;


     /**
     * 
     * @param cfg 配置参数
     * cfg.positions 围墙起始点位置；
     * cfg.color 颜色；
     * cfg.time 扫描速度；
     * cfg.height 波纹围墙高度；
     * cfg.altitude 波纹围墙原始海拔高度；
     * cfg.displayOnTop 是否展示在最上层。
     * @example let RippleWall = new GE.RippleWall(cfg);
    */
    constructor(cfg) {
        super();
        this.color = (cfg.color === undefined) ? 'ff0000' : cfg.color;
        this.center = cfg.center;
        this.displayOnTop = (cfg.displayOnTop === undefined) ? false : cfg.displayOnTop;
        this.time = (cfg.time === undefined) ? 1000 : cfg.time;
        this.height = (cfg.height === undefined) ? 50 : cfg.height;
        this.altitude = (cfg.altitude === undefined) ? 1 : cfg.altitude;
        this.num = (cfg.num === undefined) ? 1 : cfg.num;
        this.positions = cfg.positions ? this.getEPSGWebPositions(cfg.positions) : [];      
        this.init();

        let scope = this;
        Helper.addPropertyListener(scope, 'height', {
            setter: (value) => { 
                scope.uniforms.height.value = value; 
                scope.dispose();
                scope.init();
            },
            getter: () => {
                return scope.uniforms.height.value;
            }
        });

        Helper.addPropertyListener(scope, 'altitude', {
            setter: (value) => {             
                scope.uniforms.altitude.value = value;
                scope.dispose();
                scope.init();
            },
            getter: () => {
                return scope.uniforms.altitude.value;
            }
        });
        
        Helper.addPropertyListener(scope, 'v_color', {            
            setter: (value) => {
                scope.color = value;
                scope.uniforms.v_color.value = new Color(scope.color);                
             },
            getter: () => {
                return scope.color;
            }
        });
    }
    /**
     * 设置动效组件Shader参数
    */
    private setUniforms() {
        this.uniforms = {
            v_color: { type: 'c', value: new Color(this.color) },  
            height: { type: 'f', value: this.height },
            altitude: { type: 'f', value: this.altitude },
            time: { type: 'f', value: 0.0 },
            num: { type: 'f', value: this.num },
        };

        if (App.getInstance().background.fog) {
            this.uniforms.fogColor = { type: "c", value: App.getInstance().background.fog.color };
            this.uniforms.fogNear = { type: "c", value: App.getInstance().background.fog.near };
            this.uniforms.fogFar = { type: "c", value: App.getInstance().background.fog.far };
        }
    }

    /**
     * WGS84值转换为WebMercator值
    */
    protected getEPSGWebPositions(points: any) {
        let newPoints = [];
        for (let i = 0; i < points.length; i++) {
            newPoints.push(new Point(points[i][0], points[i][1], 0, Const.EPSGType.EPSG84).toEPSGWeb());
        }
        return newPoints;
    }

    /**
    * 实现效果，构建顶点、像素着色器
    */
    private init() {
        this.setUniforms();
        let material = new ShaderMaterial({
            uniforms: this.uniforms,
            vertexShader: RippleWallShader.getVertexShader(),
            fragmentShader: RippleWallShader.getFragmentShader(),
            side: DoubleSide,
            transparent: true,
            fog: true
        });

        for (let i = 0; i < (this.positions.length)-1; i++) {

            let p0 = this.positions[i];
            let p1 = this.positions[i + 1];
            if (i == this.positions.length - 1) {
                p1 = this.positions[0];
            }
            this.center = [(p0.x + p1.x) / 2, (p0.y + p1.y) / 2, this.altitude];
            var deltaY = p1.y - p0.y;
            var deltaX = p1.x - p0.x;
            if (Math.abs(deltaX) <= 0.00001) {
                if (deltaX > 0.0) {
                    deltaX = 0.00001;
                }
                else {
                    deltaX = -0.00001;
                }
            }

            let alpha = 0;
            if (deltaX >= 0) {
                alpha = Math.atan(deltaY / deltaX);
            }
            else {
                alpha = Math.PI + Math.atan(deltaY / deltaX);
            }
            let width = Math.sqrt(deltaY * deltaY + deltaX * deltaX);

            let geo = new PlaneGeometry(width, this.height, 32, 32);
            //altitude添加一个高度一半的偏移，因为我们在x轴上旋转了90度，但是要保证对象贴地
            let centerVector = new Point(this.center[0], this.center[1], this.altitude + this.height / 2, Const.EPSGType.EPSGWeb);
            let planeMesh = new Mesh(geo, material);
            planeMesh.rotation['z'] = alpha;
            planeMesh.rotateX(Math.PI / 2);
            planeMesh.position.set(centerVector.x, centerVector.y, centerVector.z);
            //如果让标记始终显示在矢量图层上，应同时关闭深度检测并将它的渲染顺序设置的比矢量图层大。
            //深度检测不关闭的话，无论渲染顺序设置为多少，矢量图层的建筑始终离相机更近
            if (this.displayOnTop) {
                planeMesh.material.depthTest = true;
                planeMesh.renderOrder = 99;
            }
            planeMesh.userData.isEffectMesh = true;
            planeMesh.userData.effect = this;
            this.add(planeMesh);
            this.planeMeshs.push(planeMesh);
        }

        this.register();
    }

    /**
     * 注册事件
    */
    protected register() {
        super.register();
        this.app.on(Events.Render, this.updateUniforms, this);
    }

    /**
     * 释放事件
    */
    public dispose() {
        this.app.off(Events.Render, this.updateUniforms, this);
        for (let i = 0; i < this.planeMeshs.length; i++) {
            this.remove(this.planeMeshs[i]);
        }
    }

    /**
     * 更新动效组件Shader参数
    */
    protected updateUniforms() {
        var now = new Date().getTime();
        this.uniforms.time.value = (now % this.time) / this.time;
        
        this.uniforms.num.value = this.num;


    }
}
